"""
回测验证系统
计算实际胜率、夏普比率、最大回撤等客观指标
"""

import pandas as pd
import numpy as np
from typing import Dict, List, Tuple, Optional
from datetime import datetime, timedelta
import warnings
import yfinance as yf
warnings.filterwarnings('ignore')

class BacktestingSystem:
    """
    回测验证系统
    用于验证MACD策略的实际表现
    """
    
    def __init__(self, initial_capital: float = 100000):
        """
        初始化回测系统
        
        Args:
            initial_capital: 初始资金
        """
        self.initial_capital = initial_capital
        self.trades = []
        self.portfolio_values = []
        self.daily_returns = []
        
    def run_backtest(self, stock_data: pd.DataFrame, signals: List[Dict], 
                    commission_rate: float = 0.001) -> Dict:
        """
        运行回测
        
        Args:
            stock_data: 股票历史数据
            signals: 交易信号列表
            commission_rate: 佣金费率
            
        Returns:
            回测结果
        """
        capital = self.initial_capital
        position = 0  # 持仓股数
        entry_price = 0
        entry_date = None
        
        # 重置记录
        self.trades = []
        self.portfolio_values = []
        self.daily_returns = []
        
        # 按日期排序信号
        signals_df = pd.DataFrame(signals)
        if not signals_df.empty:
            signals_df['date'] = pd.to_datetime(signals_df['date'])
            signals_df = signals_df.sort_values('date')
        
        # 遍历每个交易日
        for date, row in stock_data.iterrows():
            current_price = row['Close']
            
            # 检查是否有信号
            day_signals = signals_df[signals_df['date'].dt.date == date.date()] if not signals_df.empty else pd.DataFrame()
            
            for _, signal in day_signals.iterrows():
                if signal['signal_type'] == 'golden_cross' and position == 0:
                    # 买入信号
                    shares_to_buy = int(capital * 0.95 / current_price)  # 95%资金买入
                    if shares_to_buy > 0:
                        cost = shares_to_buy * current_price * (1 + commission_rate)
                        if cost <= capital:
                            capital -= cost
                            position = shares_to_buy
                            entry_price = current_price
                            entry_date = date
                            
                elif signal['signal_type'] == 'death_cross' and position > 0:
                    # 卖出信号
                    proceeds = position * current_price * (1 - commission_rate)
                    capital += proceeds
                    
                    # 记录交易
                    trade_return = (current_price - entry_price) / entry_price
                    holding_days = (date - entry_date).days
                    
                    trade_record = {
                        'entry_date': entry_date,
                        'exit_date': date,
                        'entry_price': entry_price,
                        'exit_price': current_price,
                        'shares': position,
                        'return': trade_return,
                        'return_pct': trade_return * 100,
                        'holding_days': holding_days,
                        'profit_loss': proceeds - (position * entry_price * (1 + commission_rate)),
                        'signal_quality': signal.get('quality', 'unknown')
                    }
                    self.trades.append(trade_record)
                    
                    position = 0
                    entry_price = 0
                    entry_date = None
            
            # 计算当日组合价值
            portfolio_value = capital + (position * current_price if position > 0 else 0)
            self.portfolio_values.append({
                'date': date,
                'portfolio_value': portfolio_value,
                'cash': capital,
                'position_value': position * current_price if position > 0 else 0
            })
        
        # 计算日收益率
        portfolio_df = pd.DataFrame(self.portfolio_values)
        portfolio_df['daily_return'] = portfolio_df['portfolio_value'].pct_change()
        self.daily_returns = portfolio_df['daily_return'].dropna().tolist()
        
        # 计算回测指标
        metrics = self._calculate_performance_metrics(portfolio_df)
        
        return {
            'trades': self.trades,
            'portfolio_values': self.portfolio_values,
            'performance_metrics': metrics,
            'final_capital': portfolio_df['portfolio_value'].iloc[-1] if not portfolio_df.empty else self.initial_capital
        }
    
    def _calculate_performance_metrics(self, portfolio_df: pd.DataFrame) -> Dict:
        """
        计算绩效指标
        
        Args:
            portfolio_df: 组合价值数据
            
        Returns:
            绩效指标
        """
        if portfolio_df.empty or len(self.trades) == 0:
            return self._get_empty_metrics()
        
        # 基础指标
        initial_value = portfolio_df['portfolio_value'].iloc[0]
        final_value = portfolio_df['portfolio_value'].iloc[-1]
        total_return = (final_value - initial_value) / initial_value
        
        # 交易统计
        winning_trades = [t for t in self.trades if t['return'] > 0]
        losing_trades = [t for t in self.trades if t['return'] <= 0]
        
        win_rate = len(winning_trades) / len(self.trades) if self.trades else 0
        avg_win = np.mean([t['return'] for t in winning_trades]) if winning_trades else 0
        avg_loss = np.mean([t['return'] for t in losing_trades]) if losing_trades else 0
        
        # 风险指标
        daily_returns = np.array(self.daily_returns)
        volatility = np.std(daily_returns) * np.sqrt(252) if len(daily_returns) > 1 else 0
        
        # 夏普比率（假设无风险利率为3%）
        risk_free_rate = 0.03
        excess_returns = daily_returns - risk_free_rate/252
        sharpe_ratio = np.mean(excess_returns) / np.std(excess_returns) * np.sqrt(252) if np.std(excess_returns) > 0 else 0
        
        # 最大回撤
        cumulative_returns = (1 + portfolio_df['daily_return'].fillna(0)).cumprod()
        rolling_max = cumulative_returns.expanding().max()
        drawdowns = (cumulative_returns - rolling_max) / rolling_max
        max_drawdown = drawdowns.min()
        
        # 卡尔玛比率
        calmar_ratio = total_return / abs(max_drawdown) if max_drawdown != 0 else 0
        
        # 盈亏比
        profit_loss_ratio = abs(avg_win / avg_loss) if avg_loss != 0 else float('inf')
        
        # 按信号质量分析
        quality_analysis = self._analyze_by_signal_quality()
        
        return {
            'total_return': total_return,
            'total_return_pct': total_return * 100,
            'annualized_return': (1 + total_return) ** (252 / len(portfolio_df)) - 1 if len(portfolio_df) > 0 else 0,
            'volatility': volatility,
            'sharpe_ratio': sharpe_ratio,
            'max_drawdown': max_drawdown,
            'max_drawdown_pct': max_drawdown * 100,
            'calmar_ratio': calmar_ratio,
            'win_rate': win_rate,
            'win_rate_pct': win_rate * 100,
            'total_trades': len(self.trades),
            'winning_trades': len(winning_trades),
            'losing_trades': len(losing_trades),
            'avg_win': avg_win,
            'avg_win_pct': avg_win * 100,
            'avg_loss': avg_loss,
            'avg_loss_pct': avg_loss * 100,
            'profit_loss_ratio': profit_loss_ratio,
            'avg_holding_days': np.mean([t['holding_days'] for t in self.trades]) if self.trades else 0,
            'quality_analysis': quality_analysis
        }
    
    def _analyze_by_signal_quality(self) -> Dict:
        """
        按信号质量分析表现
        
        Returns:
            按质量分组的分析结果
        """
        if not self.trades:
            return {}
        
        quality_groups = {}
        for trade in self.trades:
            quality = trade['signal_quality']
            if quality not in quality_groups:
                quality_groups[quality] = []
            quality_groups[quality].append(trade)
        
        analysis = {}
        for quality, trades in quality_groups.items():
            winning_trades = [t for t in trades if t['return'] > 0]
            
            analysis[quality] = {
                'total_trades': len(trades),
                'win_rate': len(winning_trades) / len(trades),
                'win_rate_pct': (len(winning_trades) / len(trades)) * 100,
                'avg_return': np.mean([t['return'] for t in trades]),
                'avg_return_pct': np.mean([t['return'] for t in trades]) * 100,
                'avg_holding_days': np.mean([t['holding_days'] for t in trades])
            }
        
        return analysis
    
    def _get_empty_metrics(self) -> Dict:
        """
        返回空的指标字典
        """
        return {
            'total_return': 0,
            'total_return_pct': 0,
            'annualized_return': 0,
            'volatility': 0,
            'sharpe_ratio': 0,
            'max_drawdown': 0,
            'max_drawdown_pct': 0,
            'calmar_ratio': 0,
            'win_rate': 0,
            'win_rate_pct': 0,
            'total_trades': 0,
            'winning_trades': 0,
            'losing_trades': 0,
            'avg_win': 0,
            'avg_win_pct': 0,
            'avg_loss': 0,
            'avg_loss_pct': 0,
            'profit_loss_ratio': 0,
            'avg_holding_days': 0,
            'quality_analysis': {}
        }
    
    def generate_backtest_report(self, results: Dict) -> str:
        """
        生成回测报告
        
        Args:
            results: 回测结果
            
        Returns:
            格式化的报告文本
        """
        metrics = results['performance_metrics']
        
        report = f"""
=== MACD策略回测报告 ===

📊 整体表现:
• 总收益率: {metrics['total_return_pct']:.2f}%
• 年化收益率: {metrics['annualized_return']*100:.2f}%
• 夏普比率: {metrics['sharpe_ratio']:.3f}
• 最大回撤: {metrics['max_drawdown_pct']:.2f}%
• 卡尔玛比率: {metrics['calmar_ratio']:.3f}

📈 交易统计:
• 总交易次数: {metrics['total_trades']}
• 胜率: {metrics['win_rate_pct']:.2f}%
• 盈利交易: {metrics['winning_trades']}
• 亏损交易: {metrics['losing_trades']}
• 平均盈利: {metrics['avg_win_pct']:.2f}%
• 平均亏损: {metrics['avg_loss_pct']:.2f}%
• 盈亏比: {metrics['profit_loss_ratio']:.2f}
• 平均持仓天数: {metrics['avg_holding_days']:.1f}天

⚡ 风险指标:
• 年化波动率: {metrics['volatility']*100:.2f}%

📋 按信号质量分析:
"""
        
        for quality, analysis in metrics['quality_analysis'].items():
            report += f"""
• {quality.upper()}质量信号:
  - 交易次数: {analysis['total_trades']}
  - 胜率: {analysis['win_rate_pct']:.2f}%
  - 平均收益: {analysis['avg_return_pct']:.2f}%
  - 平均持仓: {analysis['avg_holding_days']:.1f}天
"""
        
        return report
    
    def compare_with_benchmark(self, stock_data: pd.DataFrame, results: Dict) -> Dict:
        """
        与买入持有策略对比
        
        Args:
            stock_data: 股票数据
            results: 回测结果
            
        Returns:
            对比结果
        """
        # 计算买入持有收益
        start_price = stock_data['Close'].iloc[0]
        end_price = stock_data['Close'].iloc[-1]
        buy_hold_return = (end_price - start_price) / start_price
        
        # 计算买入持有的波动率
        buy_hold_returns = stock_data['Close'].pct_change().dropna()
        buy_hold_volatility = buy_hold_returns.std() * np.sqrt(252)
        
        # 计算买入持有的最大回撤
        cumulative_prices = stock_data['Close'] / stock_data['Close'].iloc[0]
        rolling_max = cumulative_prices.expanding().max()
        buy_hold_drawdowns = (cumulative_prices - rolling_max) / rolling_max
        buy_hold_max_drawdown = buy_hold_drawdowns.min()
        
        strategy_return = results['performance_metrics']['total_return']
        strategy_volatility = results['performance_metrics']['volatility']
        strategy_max_drawdown = results['performance_metrics']['max_drawdown']
        
        return {
            'strategy_return': strategy_return * 100,
            'buy_hold_return': buy_hold_return * 100,
            'excess_return': (strategy_return - buy_hold_return) * 100,
            'strategy_volatility': strategy_volatility * 100,
            'buy_hold_volatility': buy_hold_volatility * 100,
            'strategy_max_drawdown': strategy_max_drawdown * 100,
            'buy_hold_max_drawdown': buy_hold_max_drawdown * 100,
            'strategy_sharpe': results['performance_metrics']['sharpe_ratio'],
            'outperformed': strategy_return > buy_hold_return
        }
    
    def monte_carlo_simulation(self, num_simulations: int = 1000) -> Dict:
        """
        蒙特卡洛模拟
        
        Args:
            num_simulations: 模拟次数
            
        Returns:
            模拟结果统计
        """
        if not self.daily_returns:
            return {}
        
        daily_returns = np.array(self.daily_returns)
        mean_return = np.mean(daily_returns)
        std_return = np.std(daily_returns)
        
        simulation_results = []
        
        for _ in range(num_simulations):
            # 生成随机收益序列
            random_returns = np.random.normal(mean_return, std_return, len(daily_returns))
            
            # 计算累积收益
            cumulative_return = np.prod(1 + random_returns) - 1
            simulation_results.append(cumulative_return)
        
        simulation_results = np.array(simulation_results)
        
        return {
            'mean_return': np.mean(simulation_results) * 100,
            'std_return': np.std(simulation_results) * 100,
            'percentile_5': np.percentile(simulation_results, 5) * 100,
            'percentile_25': np.percentile(simulation_results, 25) * 100,
            'percentile_50': np.percentile(simulation_results, 50) * 100,
            'percentile_75': np.percentile(simulation_results, 75) * 100,
            'percentile_95': np.percentile(simulation_results, 95) * 100,
            'probability_positive': np.sum(simulation_results > 0) / len(simulation_results) * 100
        }


def main():
    """主函数 - 回测系统演示"""
    print("回测验证系统演示")
    print("="*30)
    
    # 获取示例数据
    print("\n获取AAPL股票数据...")
    try:
        ticker = yf.Ticker("AAPL")
        data = ticker.history(period="1y")
        print(f"获取到 {len(data)} 条数据")
    except Exception as e:
        print(f"获取数据失败: {e}")
        return
    
    # 创建示例信号
    signals = [
        {'date': data.index[10], 'signal_type': 'golden_cross', 'quality': 'high'},
        {'date': data.index[30], 'signal_type': 'death_cross', 'quality': 'high'},
        {'date': data.index[50], 'signal_type': 'golden_cross', 'quality': 'medium'},
        {'date': data.index[80], 'signal_type': 'death_cross', 'quality': 'medium'}
    ]
    
    # 运行回测
    bt_system = BacktestingSystem(initial_capital=100000)
    results = bt_system.run_backtest(data, signals)
    
    # 生成报告
    report = bt_system.generate_backtest_report(results)
    print(report)
    
    # 与基准对比
    benchmark_comparison = bt_system.compare_with_benchmark(data, results)
    print("\n与基准对比:")
    print(f"策略收益: {benchmark_comparison['strategy_return']:.2f}%")
    print(f"买入持有收益: {benchmark_comparison['buy_hold_return']:.2f}%")
    print(f"超额收益: {benchmark_comparison['excess_return']:.2f}%")

if __name__ == "__main__":
    main()